package com.whxd.framework.utils;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p><i>Copyright(c) 2008-2010 the original author or authors<br>
 * 
 * @version 1.0 (2008-8-3)
 * 
 * @author <a href="mailto:34205037@qq.com">王健</a>
 */
public final class StringUtil{

	private static final Pattern SIGNED_NUMBER_REGEX = Pattern.compile("^(-|\\+)?\\d+(\\.\\d+)?$");
	
	private static final Pattern UNSIGNED_NUMBER_REGEX = Pattern.compile("\\d+(\\.\\d+)?$");
	
	private static final Pattern SIGNED_INTEGER_REGEX = Pattern.compile("^(-|\\+)?\\d+$");
	
	private static final Pattern UNSIGNED_INTEGER_REGEX = Pattern.compile("\\d+$");
	
	private static final int GB_DIFF = 160;
	
	private static final int[] POS_VALUES = {
		1601,1637,1833,2078,2274,2302,2433,2594,2787,3106,3212,3472,
		3635,3722,3730,3858,4027,4086,4390,4558,4684,4925,5249,5600
    };

	private static final char[] FIRST_LETTER = {
		'a','b','c','d','e','f','g','h','j','k','l','m','n','o','p','q','r','s','t','w','x','y','z'
	};
	
	/**
	 * 通过给定字符串参数获得拼音码(不是汉字或无法找到匹配读音的汉字不被处理).
	 * @param string 源字符串
	 * @return 拼音码字符串.
	 */
	public static String pinyinCode(String string){
		return pinyinCode(string,null,true);
	}
	
	/**
	 * 通过给定字符串参数获得拼音码.
	 * @param string 源字符串
	 * @param unmatchedReplacement 用于替换不能找到匹配读音的汉字,如果此参数为null,将保留无法匹配读音的汉字
	 * @param allowNonChinachar 是否保留非中文字符
	 * @return 拼音码字符串.
	 */
	public static String pinyinCode(String string,String unmatchedReplacement,boolean allowNonChineseChar){
		StringBuffer buffer = new StringBuffer();

		for (int i = 0,length = string.length();i < length;i++){
			String every = string.substring(i,i + 1);
			
			byte[] unicode = every.getBytes();
			if (unicode[0] > 0 && unicode[0] < 128){
				if (allowNonChineseChar) buffer.append(every);
				continue;
			}

			for (int k = 0;k < unicode.length;k++){
				unicode[k] = (byte)(unicode[k] - GB_DIFF);
			}
			
			String result = unmatchedReplacement == null?every : unmatchedReplacement;
			
			int pos = unicode[0] * 100 + unicode[1];

			for (int k = 0,kLength = POS_VALUES.length - 1;k < kLength;k++){
				if (pos >= POS_VALUES[k] && pos < POS_VALUES[k + 1]){
					result = String.valueOf(FIRST_LETTER[k]);
					break;
				}
			}
			buffer.append(result);
		}
		return buffer.toString();
	}
	
	/**
	 * 判断字符串是否有长度.
	 * @param string 被判断的源字符串
	 * @return 当且仅当字符串不为null并且其长度大于0时返回true.
	 */
	public static boolean hasLength(String string){
		return string != null && string.length() > 0;
	}
	
	/**
	 * 判断字符串中是否存在有效文本.
	 * @param string 被判断的源字符串
	 * @return 当且仅当字符串不为null并且其含有非空白字符时返回true.
	 */
	public static boolean hasText(String string){
		if (!hasLength(string)){
			return false;
		}
		
		for (int i = 0,length = string.length();i < length;i++){
			if (!Character.isWhitespace(string.charAt(i))){
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * 从给定的path中获得文件名,该方法不管path是否非法或存在,直接截取最后一个文件分隔符以后的部分
	 * @param path	路径
	 * @return 从路径中得到的文件名,如果不存在文件分隔符,则原样返回参数path
	 */	
	public static String getFileName(String path){
		int separatorIndex = path.lastIndexOf(File.separator);
		return (separatorIndex != -1?path.substring(separatorIndex + 1):path);
	}
	
	/**
	 * 将字符串Collection转换为字符串数组.
	 * @param collection
	 * @return 转换后的字符串数组.
	 */	
	public static String[] toArray(Collection<String> collection){
		return collection.toArray(new String[collection.size()]);
	}
	
	/**
	 * 将一个字符串分割成字符串数组.
	 * @param string 源字符串
	 * @param regex 分隔符的正则形式
	 * @return 分割后的字符串数组.
	 */
	public static String[] toArray(String string,String separatorRegex){
		return toArray(string,separatorRegex,true,true);
	}
	
	/**
	 * 将一个字符串转换成Long数组，如果存在无法转化的字符，抛出NumberFormatException
	 * @param string 源字符串
	 * @param regex 分隔符的正则形式
	 * @return 分割后的Long数组.
	 * @throws NumberFormatException
	 */
	public static List<Long> toLongList(String string,String separatorRegex) throws NumberFormatException{
		String[] ts = string.split(separatorRegex);
		List<Long> results = new ArrayList<Long>();
		for (String t : ts){
			results.add(Long.parseLong(t));
		}
		
		return results;
	}
	
	public static Long[] toLongArray(String string,String separatorRegex) throws NumberFormatException{
		List<Long> results = toLongList(string,separatorRegex);
		return results.toArray(new Long[results.size()]);
	}
	
	/**
	 * 将一个字符串分割成字符串数组.
	 * @param string 源字符串
	 * @param regex 分隔符的正则形式
	 * @param trimTokens 是否清除被分割子串的左右空格
	 * @param ignoreEmptyTokens	是否忽略空白字符串
	 * @return 分割后的字符串数组.
	 */	
	public static String[] toArray(String string,String separatorRegex,boolean trimTokens,boolean ignoreEmptyTokens){		
		List<String> list = new ArrayList<String>();
		
		Pattern p = Pattern.compile(separatorRegex);
		Matcher m = p.matcher(string);
		while (m.find()){
			String s = string.substring(0,m.start()); 
			if (trimTokens) s = s.trim();
			
			if (!ignoreEmptyTokens || s.length() > 0){
				list.add(s);
			}
			
			string = string.substring(m.start() + 1);
			m = p.matcher(string);
		}
		
		if (trimTokens) string = string.trim();
		if (!ignoreEmptyTokens || string.length() > 0){
			list.add(string);
		}
		
		return toArray(list);
	}
	
	/**
	 * 将源字符串中的占位符用参数数组中的对应值替换.
	 * 对应于JDK1.5中的java.lang.String.format(...)，只是为了1.4及以下版本提供一个方便的格式化方法.
	 * 在源字符串中，使用E-L表达式作为占位符，该E-L表达式中的键值必须为数字，否则不会被作为占位符处理.
	 * 以E-L表达式中的键值作为下标，使用对象(参数replacement数组中对应下标的对象)toString方法的返回值替换占位符.
	 * 在进行占位符替换时，忽略java.lang.ArrayIndexOutOfBoundsException,java.lang.NullPointerException,
	 * 在上述两种异常情况下，占位符将无法被正常替换，所以想进行有效替换应保证参数的正常有效。
	 * @param string 将被处理的源字符串 例："我的名字叫${1},来自${0}."
	 * @param args 用来替换源字符串中有效占位符的参数 例：new String[]{"山东威海","Garfield"}
	 * @return format完成后的字符串
	 */
	public static String format(String string,Object[] replacement){
		StringBuffer buffer = new StringBuffer();
		
		Pattern p = Pattern.compile("\\$\\{\\d+\\}");
		Matcher m = p.matcher(string);
		while (m.find()){
			String field = m.group();
			try
			{
				m.appendReplacement(buffer,replacement[Integer.parseInt(field.substring(2,field.length() - 1).trim())].toString());
			}
			catch (java.lang.ArrayIndexOutOfBoundsException idxOut){
				//ignore java.lang.ArrayIndexOutOfBoundsException
			}
			catch (java.lang.NullPointerException nullPointer){
				//ignore java.lang.NullPointerException
			}
		}
		
		m.appendTail(buffer);
		return buffer.toString();
	}
	
	/**
	 * 清除一个字符串左边的空格.
	 * @param string 源字符串
	 * @return 处理后的字符串.
	 */	
	public static String leftTrim(String string){
		return string.replaceAll("^\\s+","");
	}
	
	/**
	 * 清除一个字符串右边的空格.
	 * @param string 源字符串
	 * @return 处理后的字符串.
	 */
	public static String rightTrim(String string){
		return string.replaceAll("\\s+$","");
	}
	
	/**
	 * 清除一个字符串中所有的空格.
	 * @param string 源字符串
	 * @return 处理后的字符串.
	 */ 
	public static String allTrim(String string){
		return string.replaceAll("\\s","");
	}
	
	/**
	 * 将字符串数组转换为一个字符串.
	 * @param array 源字符串数组,以空格进行分隔.
	 * @return 处理后的字符串.
	 */
	public static String asString(String[] array){
		return asString(array,"\""," ");
	}
	
	/**
	 * 将字符串数组转换为一个字符串.
	 * @param array 源字符串数组.
	 * @param sheath 转换后每个数组元素的外壳符号.
	 * @param split 转换后数组元素之间的分隔符.
	 * @return 处理后的字符串.
	 */
	public static String asString(String[] array,String sheath,String split){
		StringBuffer b = new StringBuffer();
		if (array != null){
			for (int i = 0,length = array.length;i < length;i++){
				b.append(String.format("%2$s%s%2$s",array[i],sheath));
				if (i < length - 1){
					b.append(split);
				}
			}
		}
		return b.toString();
	}
	
	/**
	 * 判断一个字符串是否符合数字格式,允许带'+'或者'-'号,允许带小数点.
	 * @param number
	 * @return 符合数字格式返回true,否则返回false.
	 */
	public static boolean isNumber(String number){
		return isNumber(number,true);
	}
	
	/**
	 * 判断一个字符串是否符合数字格式,允许带小数点.
	 * @param string
	 * @param signed 是否允许带符号
	 * @return 符合数字格式返回true,否则返回false.
	 */
	public static boolean isNumber(String string,boolean signed){
		return signed ? SIGNED_NUMBER_REGEX.matcher(string).matches() : UNSIGNED_NUMBER_REGEX.matcher(string).matches();
	}
	
	/**
	 * 判断一个字符串是否符合整数格式,允许前面带一个'+'或'-'符号.
	 * @param integer
	 * @return 符合整数格式返回true,否则返回false.
	 */
	public static boolean isInteger(String string){
		return isInteger(string,true);
	}
	
	/**
	 * 判断一个字符串是否符合整数格式.
	 * @param string
	 * @param signed 是否允许带符号
	 * @return 符合整数格式返回true,否则返回false.
	 */
	public static boolean isInteger(String string,boolean signed){
		return signed ? SIGNED_INTEGER_REGEX.matcher(string).matches() : UNSIGNED_INTEGER_REGEX.matcher(string).matches();
	}
	
	public static String hexString(byte[] bytes){
		StringBuffer sb = new StringBuffer();
		for (byte b : bytes){
			String hex = Integer.toHexString(b & 0xFF);
			sb.append(hex.length() == 1?"0" + hex:hex);
		}
		return sb.toString();
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	/**
	 * md5或者sha-1加密
	 * 
	 * @param inputText
	 *            要加密的内容
	 * @param algorithmName
	 *            加密算法名称：md5或者sha-1，不区分大小写
	 * @return
	 */
	private static String encrypt(String inputText, String algorithmName) {
		if (inputText == null || "".equals(inputText.trim())) {
			throw new IllegalArgumentException("请输入要加密的内容");
		}
		if (algorithmName == null || "".equals(algorithmName.trim())) {
			algorithmName = "md5";
		}
		String encryptText = null;
		try {
			MessageDigest m = MessageDigest.getInstance(algorithmName);
			m.update(inputText.getBytes("UTF8"));
			byte s[] = m.digest();
			// m.digest(inputText.getBytes("UTF8"));
			return hex(s);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return encryptText;
	}

	/**
	 * 返回yyyy-MM-dd格式的当前日期
	 * 
	 * @return String
	 */
	public static String getCuttentDate() {

		SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd");
		return dataFormat.format(new Date());
	}

	/**
	 * 返回yyyy-MM-dd HH:mm:ss格式的当前时间
	 * 
	 * @return String
	 */
	public static String getCuttentTime() {

		SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return dataFormat.format(new Date());
	}

	/**
	 * 
	 * 返回传入参数格式的当前时间或者日期
	 * 
	 * @param formatStr
	 *            时间格式的参数如yyyy-MM-dd hh:mm:ss
	 * @return String
	 */
	public static String getCuttentTime(String formatStr) {

		SimpleDateFormat dataFormat = new SimpleDateFormat(formatStr);
		return dataFormat.format(new Date());
	}

	/**
	 * 
	 * 返回传入时间字符串和格式参数的格式化时间
	 * 
	 * @param time
	 *            时间字符串2014-05-16 12:12:22
	 * @param formatStr
	 *            时间格式的参数如yyyy-MM-dd hh:mm:ss
	 * @return String
	 * @throws ParseException
	 */
	public static String getFormatTime(String time, String formatStr) throws ParseException {

		SimpleDateFormat dataFormat = new SimpleDateFormat(formatStr);
		Date date = dataFormat.parse(time);
		return dataFormat.format(date);
	}

	/**
	 * 根据给定的长度，生成一个随机的｛a-z,A-Z,0-9｝规定程度的字符串。
	 * 
	 * @param length
	 *            生成字符串的长度,length应该大约0，如果length<1,则length=6
	 * @return returnStr 给定长度的随机字符串
	 */
	public static String getRandomStr(int length) {
		if (length < 1) {
			length = 6;
		}
		String[] strs = new String[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };

		Random random = new Random();
		String returnStr = "";
		int randomInt = 0;
		for (int i = 0; i < length; i++) {
			randomInt = random.nextInt(61);
			returnStr += strs[randomInt];
		}

		// System.out.println("生成长度为 "+length+" 的字符串："+returnStr);
		return returnStr;
	}

	/**
	 * 根据给定的明文密码和加密工具返回加密后的字符串
	 * 
	 * @param password
	 *            密码明文
	 * @param saltStr
	 *            密码盐值
	 * @return passwordStr 返回经过md5(md5(password)+saltStr)加工过后的string<br>
	 *         即密码的密文
	 */
	public static String getSaltPasswordStr(String password, String saltStr) {
		String passwordStr;

		passwordStr = md5(password);
		passwordStr = md5(passwordStr + saltStr);

		return passwordStr;

	}


	/*	*//**
	 * 根据给定的明文密码和加密工具返回加密后的字符串
	 * 
	 * @param password
	 *            密码明文
	 * @param saltStr
	 *            密码盐值
	 * @param passwordEncoder
	 *            加密工具类（SpringSecurity的密码加密类）
	 * @return passwordStr
	 *         返回经过PasswordEncoder.encodePassword(PasswordEncoder.encodePassword
	 *         (passwordStr)+saltStr)加工过后的string<br>
	 *         即密码的密文
	 */
	/*
	 * public static String getSaltPasswordStr(String password, String saltStr,
	 * PasswordEncoder passwordEncoder) { String passwordStr;
	 * 
	 * passwordStr = passwordEncoder.encodePassword(password, null); passwordStr
	 * = passwordEncoder.encodePassword(passwordStr, saltStr);
	 * 
	 * return passwordStr;
	 * 
	 * }
	 */
	/**
	 * 获得一个32位（无‘-’）UUID
	 * 
	 * @return String UUID
	 */
	public static String getUUID() {
		String s = UUID.randomUUID().toString();
		// 去掉“-”符号
		return s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);
	}

	/**
	 * 获得指定数目的UUID 32位（无‘-’）
	 * 
	 * @param number
	 *            int 需要获得的UUID数量
	 * @return String[] UUID数组
	 */
	public static String[] getUUID(int number) {
		if (number < 1) {
			return null;
		}
		String[] ss = new String[number];
		for (int i = 0; i < number; i++) {
			ss[i] = getUUID();
		}
		return ss;
	}

	/**
	 * 返回十六进制字符串
	 * 
	 * @param arr
	 * @return
	 */
	private static String hex(byte[] arr) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < arr.length; ++i) {
			sb.append(Integer.toHexString((arr[i] & 0xFF) | 0x100).substring(1, 3));
		}
		return sb.toString();
	}

	/**
	 * md5加密
	 * 
	 * @param inputText
	 * @return
	 */
	public static String md5(String inputText) {
		return encrypt(inputText, "md5");
	}

	/**
	 * 二次加密，应该破解不了了吧？
	 * 
	 * @param inputText
	 * @return
	 */
	public static String md5AndSha(String inputText) {
		return sha(md5(inputText));
	}

	/**
	 * sha加密
	 * 
	 * @param inputText
	 * @return
	 */
	public static String sha(String inputText) {
		return encrypt(inputText, "sha-1");
	}

	/**
	 * 根据类的对象，属性名，属性值是使用属性对应的setter方法设置该对象
	 * 
	 * @param filedName
	 *            类的属性名
	 * @param fieldValue
	 *            类的属性值
	 * @param obj
	 *            类的对象
	 * @return
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	public static Object setProperty(String filedName, Object fieldValue, Object obj) throws SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		String prop = Character.toUpperCase(filedName.charAt(0)) + filedName.substring(1);
		String mname = "set" + prop;
		Class[] types = new Class[] { fieldValue.getClass() };

		Method method;
		try {
			method = obj.getClass().getMethod(mname, types);
			method.invoke(obj, new Object[] { fieldValue });
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
		}
		return obj;
	}

	/**
	 * 根据类的对象，属性名，属性值是使用属性对应的getter方法获取属性值
	 * 
	 * @param filedName
	 *            类的属性名
	 * @param obj
	 *            类的对象
	 * @return getter方法的值
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	public static Object getProperty(String filedName, Object obj) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		String prop = Character.toUpperCase(filedName.charAt(0)) + filedName.substring(1);
		String mname = "get" + prop;
		Method method = obj.getClass().getMethod(mname);
		Object result = method.invoke(obj);
		return result;
	}


	/**
	 * 把小数四舍五入
	 * 
	 * @param d
	 *            要格式化的小数
	 * @param figure
	 *            保留几位小数；如果是整数3就返回3.0
	 * @return double
	 */
	public static double formatDouble(double d, int figure) {
		BigDecimal b = new BigDecimal(d);
		return b.setScale(figure, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 把小数四舍五入
	 * 
	 * @param d
	 *            要格式化的小数
	 * @param figure
	 *            最多保留几位小数；如果是整数3.0 就返回3
	 * @return String
	 */
	public static String formatDouble2String(double d, int figure) {
		NumberFormat nbf = NumberFormat.getInstance();
		nbf.setMinimumFractionDigits(0);
		nbf.setMaximumFractionDigits(figure);
		return nbf.format(d);
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	public static void main(String[] args){
		//System.out.println(hexString("\n".getBytes()));
		System.out.println(StringUtil.asString(new String[]{"a","b","c"},"'",","));
		System.out.println(StringUtil.pinyinCode("网络"));
		
		//测试反射
		/*
		 * Student s = new Student(); s.setId(1); s.setName("1");
		 * s.setPassword("setPassword"); System.out.println(s); Student ss; try
		 * { setProperty("name", "2", s); System.out.println(s);
		 * 
		 * Object sd = getProperty("password",s); System.out.println(sd); }
		 * catch (NoSuchMethodException e) { // TODO Auto-generated catch block
		 * e.printStackTrace(); } catch (SecurityException e) { // TODO
		 * Auto-generated catch block e.printStackTrace(); } catch
		 * (IllegalAccessException e) { // TODO Auto-generated catch block
		 * e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO
		 * Auto-generated catch block e.printStackTrace(); } catch
		 * (InvocationTargetException e) { // TODO Auto-generated catch block
		 * e.printStackTrace(); }
		 */
	}
}